package br.edu.unifei.cct720.alieninvasion.main.gui;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import br.edu.unifei.cct720.alieninvasion.entity.Entity;
import br.edu.unifei.cct720.alieninvasion.entity.alien.AlienEntity;
import br.edu.unifei.cct720.alieninvasion.entity.alien.AlienMotherShipEntity;
import br.edu.unifei.cct720.alieninvasion.entity.alien.AlienShipEntity;
import br.edu.unifei.cct720.alieninvasion.entity.alien.AlienShotEntity;
import br.edu.unifei.cct720.alieninvasion.entity.ship.ShipEntity;
import br.edu.unifei.cct720.alieninvasion.entity.ship.ShipShotEntity;
import br.edu.unifei.cct720.alieninvasion.resources.SoundEffect;
import br.edu.unifei.cct720.alieninvasion.resources.SystemTimer;
import br.edu.unifei.cct720.alieninvasion.utils.Constants;
import br.edu.unifei.cct720.alieninvasion.utils.HighestScoreFile;
import br.edu.unifei.cct720.alieninvasion.utils.Messages;

/**
 * Class to represent game scenario and execution.
 * 
 * @author Felipe Knappe
 *
 */
public class GameCanvas extends Canvas implements Runnable {

	/**
	 * The game execution thread
	 */
	Thread gameThread;

	/**
	 * The demo execution thread
	 */
	Thread demoThread;

	/**
	 * The strategy that controls the accelerate page flipping
	 */
	private BufferStrategy strategy;

	/**
	 * The list of entities presents on game
	 */
	List<Entity> entities = null;

	/**
	 * The list of entities removed from the game
	 */
	List<Entity> removeList = null;

	/**
	 * The entity representing the player
	 */
	private Entity ship;

	/**
	 * The list of alien ships presents on the game
	 */
	private List<AlienShipEntity> alienShips = null;

	/**
	 * The screen width dimension
	 */
	public int xSize = 800;

	/**
	 * The screen height dimension
	 */
	public int ySize = 600;

	/**
	 * True if we're holding up game play until a key has been pressed
	 */
	private boolean waitingForKeyPress = true;

	/**
	 * True if game is paused
	 */
	private boolean pausedGame = false;

	/**
	 * True if the left cursor key is currently pressed
	 */
	private boolean leftPressed = false;

	/**
	 * True if the right cursor key is currently pressed
	 */
	private boolean rightPressed = false;

	/**
	 * True if we are firing
	 */
	private boolean firePressed = false;

	/**
	 * True when game started
	 */
	private boolean gameStarted = false;

	/**
	 * True if ship is dead
	 */
	private boolean gameOver = false;

	/**
	 * True if player won the game
	 */
	private boolean gameWin = false;

	/**
	 * True if player go to next level
	 */
	private boolean goNextLevel = false;

	/**
	 * True if ship was hitted but not killed
	 */
	private boolean shipHitted = false;

	/**
	 * True if some logic occurred throw some thread execution
	 */
	private boolean logicRequiredThisLoop = false;

	/**
	 * The x-coordinates of each star on the background starfield
	 */
	public int starsX[];

	/**
	 * The y-coordinates of each star on the background starfield
	 */
	public int starsY[];

	/**
	 * The number of the stars present on the background starfield
	 */
	private int numStars = 120;

	/**
	 * The time at which last fired a shot
	 */
	private long lastFire = 0;

	/**
	 * The interval between our players shot (ms)
	 */
	private long shipFiringInterval = 500;

	/**
	 * The interval between alien ships shot (ms)
	 */
	private long alienFiringInterval = 2000;

	/**
	 * The interval between aliens ships flying (ms)
	 */
	private long alienFlyingInterval = 8000;

	/**
	 * When last flying alien ships occurs
	 */
	private long lastAlienShipFlying = 0;

	/**
	 * The message to display which waiting for a key press
	 */
	private String message = "";

	/**
	 * The player score
	 */
	private int score = 0;

	/**
	 * The speed at which the player's ship should move (pixels/sec)
	 */
	private double moveSpeed = 300;

	/**
	 * The interval between each game loop execution
	 */
	private int sleepTime = 8;

	/**
	 * The number of aliens entities
	 */
	private int numberOfAliens;

	/**
	 * The number of alien ships entity
	 */
	private int numberOfAlienShips;

	/**
	 * The number of enemies killed by the player
	 */
	private int enemiesEliminated;

	/**
	 * The current game level
	 */
	private int gameLevel = 1;

	/**
	 * The quantity of score needed to win a new live
	 */
	private int scorePerLive;

	/**
	 * True if player won score for gift lifes
	 */
	private int ownScorePerLive = 0;

	/** 
	 * The number of key presses we've had while waiting for an "any key" press 
	 */
	private int pressCount = 1;

	/**
	 * The number of player's lives
	 */
	private int lives = 3;

	/**
	 * The number of shots to kill the mothership
	 */
	private int motherShipHits;

	/**
	 * The number of shots to kill the alien ship
	 */
	private int alienShipHits;

	/**
	 * Class Constructor
	 * Create a new game canvas.
	 * 
	 */
	public GameCanvas() {
		/*
		 * Tell AWT not to bother repainting our canvas since we're
		 * going to do that our self in accelerated mode
		 */
		setIgnoreRepaint(true);

		// Adding key event listener to the canvas
		this.addKeyListener(new KeyInputHandler());
	}

	/**
	 * Method responsible to create the starfield 
	 * in the background.
	 * 
	 */
	private void initStars () {

		// Initializing variables
		starsX = new int[numStars];
		starsY = new int[numStars];

		// Loop to set the (x, y) coordinate of each star
		for (int a = 0; a < numStars; a++) {
			starsX[a] = (int) ((Math.random() * xSize - 1) + 1);
			starsY[a] = (int) ((Math.random() * ySize - 1) + 1);
		}
	}

	/**
	 * Method responsible to initialize the starting state of the entities (ships and aliens). 
	 * Each entitiy will be added to the overall list of entities in the game.
	 * 
	 */
	private void initEntities() {

		// Initializing variables
		entities = new ArrayList<Entity>();
		alienShips = new ArrayList<AlienShipEntity>();

		// Create the player ship and place it roughly in the center of the screen
		ship = new ShipEntity(this, Constants.PLAYER_SHIP, 370, 575);
		entities.add(ship);

		// Switch between game levels to create enemies
		switch(gameLevel) {

		case 1:
			newAlienShips(true);
			newAliens();
			break;

		case 2:
			newAliens();
			break;

		case 3:
			newAlienMotherShip(325, 20);
			newAliens();
			break;

		default:
			break;
		}
	}

	/**
	 * Method resposible to load all sound
	 * samples needed on the game.
	 * 
	 */
	public void loadSoundSamples() {

		// Pre-load all the sound samples
		SoundEffect.init();

		// Un-mute load samples
		SoundEffect.volume = SoundEffect.Volume.LOW;
	}

	/**
	 * Method responsible to remove all entities
	 * specified on remove list.
	 * 
	 */
	private void removeEntities() {
		// remove any entity that has been marked for clear up
		if(removeList == null) {
			removeList = new ArrayList<Entity>();
		}
		entities.removeAll(removeList);
		removeList.clear();
	}

	/**
	 * Method responsible to remove an entity from the game. 
	 * The entity removed will no longer move or be drawn.
	 * 
	 * @param entity The entity that should be removed
	 * 
	 */
	public void removeEntity(Entity entity) {
		// remove a specific entity that has been marked for clear up
		if(removeList == null) {
			removeList = new ArrayList<Entity>();
		}
		removeList.add(entity);
	}

	/**
	 * Method responsible to remove an alien entity from the game.
	 * When an enemy was killed by the player, this method is called
	 * to update game control information.
	 * 
	 * @param entity The alien entity that will be removed
	 * 
	 */
	public void removeAlienEntity(Entity entity) {
		if(entity instanceof AlienShipEntity) {
			enemiesEliminated -= 1;
			alienShips.remove(entity);
		}
		this.updatePlayerScore(entity);	
		this.removeEntity(entity);
	}

	/**
	 * Method responsible for update the number of
	 * eliminated enemies from the game.
	 * 
	 */
	private void updateEnemiesEliminated() {

		// Check if enemies left is equal 0
		if(gameLevel <= 2) {
			if(enemiesEliminated == 0) {
				this.notifyWin();
			}
		} 
	}

	/**
	 * Method responsible to instantiate an
	 * Alien entity.
	 * 
	 */
	public void newAliens() {
		/*
		 *  Create a block of aliens (12 aliens per row, spaced evenly)
		 */
		for (int row = 0; row < numberOfAliens; row++) {
			for (int j = 0; j < 12; j++) {
				Entity alien = new AlienEntity(this, 100 + (j * 50), (50) + row * 35, 4);
				entities.add(alien);
			}
		}
	}

	/**
	 * Method responsible to instantiate an
	 * AlienShip entity.
	 * 
	 */
	public void newAlienShips(boolean stayStatic) {
		/*
		 * Create new alien ships
		 */
		for(int i = 0; i < numberOfAlienShips; i++) {
			AlienShipEntity alienShip = new AlienShipEntity(this, 225 + (i * 235), 45, stayStatic);
			alienShips.add(alienShip);
		}
		entities.addAll(alienShips);
	}

	/**
	 * Method responsible to instantiate an
	 * AlienMotherShip entity.
	 * 
	 * @param x The x-coordinate of the entity
	 * @param y The y-coordinate of the entity
	 * 
	 */
	private void newAlienMotherShip(int x, int y) {
		// Create a new alien mothership entity and add this to the game
		Entity alien = new AlienMotherShipEntity(this, x, y, true);
		entities.add(alien);
	}

	/**
	 * Method responsible to set the value
	 * of the shifts (both horizontal and vertical)
	 * on the alien entities.
	 * 
	 * @param delta The value of the shift
	 * 
	 */
	private void moveEnemiesAndAttack(long delta) {

		//Loop for each entity to move itself
		if(!waitingForKeyPress) {
			for (int i=0;i < entities.size();i++) {
				Entity entity = (Entity) entities.get(i);
				entity.move(delta);
				if(entity instanceof AlienShipEntity) {
					this.alienFireAttack((AlienShipEntity)entity);
				}
			}
		}
	}

	/**
	 * Method responsible to handle player ship 
	 * horizontal movements.
	 * 
	 */
	private void moveShip() {

		if(!waitingForKeyPress) {
			//Handling player ship movement when ship is not moving. 
			ship.setHorizontalMovement(0);

			/*
			 *  If some cursor key (right or left) is pressed then
			 *  update the movement propierly
			 */
			if ((leftPressed) && (!rightPressed)) {
				ship.setHorizontalMovement(-moveSpeed);
			} else if ((rightPressed) && (!leftPressed)) {
				ship.setHorizontalMovement(moveSpeed);
			}
		}
	}

	/**
	 * Method responsible to handle all player ship
	 * fire shoot events. 
	 * 
	 */
	public void tryToFire() {

		if(!waitingForKeyPress) {
			/*
			 *  Check if firingInterval has been long enough to fire. If not,
			 *  then don't do anything.
			 */
			if (System.currentTimeMillis() - lastFire < shipFiringInterval) {
				return;
			}

			/*
			 * Otherwise, create a new shot entity and record the time it happens
			 * to control fireInvterval.
			 */
			lastFire = System.currentTimeMillis();
			ShipShotEntity shot = new ShipShotEntity(this, Constants.PLAYER_SHOT, ship.getX() + 10, ship.getY() - 30);
			entities.add(shot);

			// After that, play player ship fire shoot sound
			SoundEffect.PLAYER_SHOT.play();

			return;
		}
	}

	/**
	 * Method responsible to handle all alien entities
	 * fire shoots.
	 * 
	 */
	public void alienFireAttack(AlienShipEntity alien) {

		if(!waitingForKeyPress) {
			/*
			 *  Check if time since last alien attack has been long enough to a new one. If not,
			 *  then don't do anything.
			 */
			if (System.currentTimeMillis() - alien.getLastAlienAttack() < alienFiringInterval) {
				return;
			}

			/*
			 * Otherwise, create a new alien shot entity and record the time it happens
			 * to control alienFiringInterval 
			 */
			alien.setLastAlienAttack(System.currentTimeMillis());
			AlienShotEntity shot = new AlienShotEntity(this, Constants.ALIEN_SHOT, alien.getX() + 20, alien.getY() + 20);
			entities.add(shot);

			// After that, play player ship fire shoot sound
			SoundEffect.ALIEN_SHOT.play();
		}
	}

	/**
	 * Method responsible to handle a new alien ships
	 * flying attack.
	 * 
	 */
	private void alienFlyingAttack() {
		if(!waitingForKeyPress) {
			/*
			 *  Check if time since last alien attack has been long enough to a new one. If not,
			 *  then don't do anything.
			 */
			if (System.currentTimeMillis() - lastAlienShipFlying < alienFlyingInterval) {
				return;
			}

			/*
			 * Otherwise, create a new alien shot entity and record the time it happens
			 * to control alienFiringInterval 
			 */
			lastAlienShipFlying = System.currentTimeMillis();
			newAlienShips(false);
		}
	}

	/**
	 * Method responsible for update player score.
	 * 
	 * @param entity
	 */
	private void updatePlayerScore(Entity entity) {

		/*
		 *  Each alien killed give 250 to player score
		 */
		if(entity instanceof AlienEntity) {
			score += 250;
		}

		/*
		 * Each alien ship give 500 to player score
		 */
		else if(entity instanceof AlienShipEntity) {
			score += 500;
		}

		/*
		 *  The big boss mothership give 10000 points to player score
		 */
		else if(entity instanceof AlienMotherShipEntity) {
			score += 10000;
		}

		this.upLives();
	}

	/**
	 * Method responsible for update players lives.
	 * 
	 */
	private void upLives() {
		/*
		 * If score is a multiple of scorePerLive,
		 * give a new live to the player
		 */
		if((score % scorePerLive) == 0) {
			lives++;
			ownScorePerLive += 1;
		}

		return;
	}

	/**
	 * Method responsible for notify that the player has died. 
	 * 
	 */
	public void notifyDeath() {
		if (lives == 1) {
			gameOver = true;
			message = "Game Over\n";
			message += "Oh no! Mission failed!";
		} else {
			shipHitted = true;
			message = "May day! May Day! You got fired.";
		}

		restartGame();
	}

	/**
	 * Method responsible for notify that the player has won since all the aliens
	 * are dead.
	 * 
	 */
	public void notifyWin() {
		message = "Well done! You Win! ";
		message += "\nGo to Level " + (gameLevel + 1);
		goNextLevel = true;
		restartGame();

	}

	/**
	 * Method responsible for notify that the game stared.
	 * 
	 */
	public void notifyGameStarted() {
		message = "Welcome to Alien Invasion!";
	}

	/**
	 * Method responsible for notify that the game is paused.
	 * 
	 */
	public void notifyGamePaused() {
		message = "Game Paused";
	}

	/**
	 * Method responsible for notify that a alien
	 * enemy was killed.
	 * 
	 */
	public void notifyEnemyKilled() {
		// When an enemy shot down, play alien explode sound
		SoundEffect.ALIEN_EXPLODE.play();
	}

	/**
	 * Method responsible for notify that the player
	 * won the game.
	 * 
	 */
	public void notifyGameFinished() {

		//Show congratulations message
		message = "Houston! Houston! Mission Complete!";
		message += "\nCongratulations, the human bean is saved!";
		message += "\nYour score was " + calculateFinalScore();

		saveScoreInformation();

		// After that, play player ship explosion sound
		SoundEffect.ALIEN_EXPLODE.play();

		gameWin = true;

		restartGame();
	}

	/**
	 * Method responsible for calculate the final score of
	 * the game player.
	 * 
	 * @return int
	 */
	private int calculateFinalScore() {
		score += score + (lives * 10000);
		return score;
	}

	/**
	 * Method responsible for tests for collisions between the sprites
	 * on the screen.
	 * 
	 */
	private void collisions () {
		/* 
		 * Brute force collisions, compare every entity against
		 * every other entity. If any of them collide notify
		 * both entities that the collision has occured
		 */
		if(!waitingForKeyPress) {
			for (int p = 0; p < entities.size(); p++) {
				for (int s = p+1 ; s < entities.size() ; s++) {
					Entity me = (Entity) entities.get(p);
					Entity him = (Entity) entities.get(s);

					if (me.collidesWith(him)) {
						me.collidedWith(him);
						him.collidedWith(me);
					}
				}
			}
		}
	}

	/**
	 * Method responsible for prepare the graphics to being draw 
	 * on the screen.
	 * 
	 */
	private void prepareStrategy() {

		Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
		g.setColor(Color.black);
		g.fillRect(0, 0, xSize, ySize);

		/*
		 * Drawing player control information
		 */
		g.setColor(Color.yellow);
		g.drawString("Score = " + score, 5, 20);
		g.drawString("Lives = " + lives, 725, 20);


		/*
		 * Drawing background
		 */
		g.setColor(Color.white);
		for(int a = 0; a < ySize; a ++) {
			for (int c = 0; c < numStars; c++) {
				g.drawRect(starsX[c], starsY[c], 1, 1);
			}
		}

		// Cycle for draw all the entities we have in the game
		for (int i = 0; i < entities.size();i++) {
			Entity entity = (Entity) entities.get(i);
			entity.draw(g);
		}

		/*
		 * If waiting for an "any key" press then draw the
		 * current message 
		 */
		if (waitingForKeyPress) {
			g.setColor(Color.white);
			g.drawString(message,(800-g.getFontMetrics().stringWidth(message))/2,250);
			g.drawString("Press any key to continue",(800-g.getFontMetrics().stringWidth("Press any key to continue"))/2,300);
		} else if(pausedGame) {
			g.setColor(Color.white);
			g.drawString(message,(800-g.getFontMetrics().stringWidth(message))/2,250);
			g.drawString("Press 'P' to restart",(800-g.getFontMetrics().stringWidth("Press 'P' to restart"))/2,300);
		}

		/*
		 * Finally, after complete drawing, clear up the graphics
		 * and send the graphic information to buffer
		 */
		g.dispose();
		strategy.show();
	}

	/**
	 * Method for handle events from a game entity that the logic of the game
	 * should be run at the next thread cycle.
	 * 
	 */
	public void updateLogic() {
		logicRequiredThisLoop = true;
	}

	/**
	 * Method responsible for start a new fresh game.
	 * 
	 */
	public void startGame() {

		// Initialize game configurations
		initGameControllers();
		initStars();
		initEntities();

		notifyGameStarted();

		/*
		 * Start the game thread and set it
		 * with highest priority
		 */
		if(gameThread == null) {
			gameThread = new Thread(this);
			gameThread.setPriority(5);
			gameThread.start();
		} 
	}

	/**
	 * Method responsible for initialize all
	 * game controller flags.
	 * 
	 */
	private void initGameControllers() {
		// blank out any keyboard settings we might currently have
		leftPressed = false;
		rightPressed = false;
		firePressed = false;
		waitingForKeyPress = true;
		pressCount = 1;

		// restore the enemies eliminated by the player from the game
		enemiesEliminated = numberOfAlienShips;

		// restore ship hit state
		shipHitted = false;

		// restore game actions
		gameWin = false;
		gameOver = false;
		goNextLevel = false;
	}

	/**
	 * Method responsible for control
	 * the game actions and restart it.
	 * 
	 */
	public void restartGame() {

		// Logic to control game execution
		if(gameOver) {
			gameLevel = 1;
			lives = 3;
			lastAlienShipFlying = 0;
			score = 0;
			ownScorePerLive = 0;
		} else if(shipHitted) {
			lives--;
			score -= scorePerLive * ownScorePerLive;
			ownScorePerLive = 0;
		} else if(gameWin) {
			gameLevel = 1;
			score = 0;
			lastAlienShipFlying = 0;
			ownScorePerLive = 0;
		} else {
			if(goNextLevel) {
				gameLevel += 1;
			}
		}

		// Recovering default values
		initGameControllers();
		initEntities();

		// Running the game
		if(gameThread == null) {
			startGame();
		} else {
			gameThread.run();
		}
	}

	/**
	 * Method responsible for pause the game thread.
	 * 
	 */
	public void pauseGame() {
		try {			
			gameThread.sleep(sleepTime);
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Method responsible for stop the current game
	 * execution.
	 * 
	 */
	public void stopGame() {
		if(gameThread != null) {
			gameThread.interrupt();
			gameThread = null;
		}
	}

	/**
	 * Method responsible for thread loop execution.
	 * 
	 */
	@Override
	public void run() {

		long lastLoopTime = SystemTimer.getTime();
		long delta = 0;

		while (true) {

			updateEnemiesEliminated();

			// This pauses the game
			if(pausedGame) {
				try {				
					while(pausedGame) {	
						SystemTimer.sleep(1);
						gameThread.sleep(1);
					}
				} catch(InterruptedException e) {
					e.printStackTrace();
				}
			}

			delta = SystemTimer.getTime() - lastLoopTime;
			lastLoopTime = SystemTimer.getTime();

			// These methods update the sprites positions
			moveEnemiesAndAttack(delta);			
			collisions();
			removeEntities();

			/*
			 * if a game entity has indicated that game logic should
			 * be resolved, loop around every entity requesting that
			 * their execute its personal logic.
			 */
			if (logicRequiredThisLoop) {
				for (int i = 0; i < entities.size();i++) {
					Entity entity = (Entity) entities.get(i);
					entity.doLogic();
				}

				logicRequiredThisLoop = false;
			}

			// This method prepares the screen to be drawn
			prepareStrategy();	

			// This method repositions player ship on screen
			moveShip(); 

			// if pressing fire, attempt to fire
			if (firePressed) {
				tryToFire();
			}

			/*
			 *  2nd and 3rd levels has
			 *  alien ships moving on dy 
			 *  direction 
			 */
			if(gameLevel > 1) {
				alienFlyingAttack();
			}

			/*
			 *  Call the thread sleep to
			 *  syncronize game frames 
			 */
			pauseGame();

			/*
			 * Each frame take 10 milliseconds to run. To do this,
			 * the elapsed time was recorded when the frame started. 
			 * and 10 milliseconds was added to this and then factor 
			 * in the current time to give us our final value to wait for
			 */
			SystemTimer.sleep(lastLoopTime+10 - SystemTimer.getTime());
		}
	}

	/**
	 * Method responsible for save player highest scores
	 */
	private void saveScoreInformation() {

		// Local variables
		HighestScoreFile file = null;
		List<Integer> highScores = null;
		
		try{
			file = new HighestScoreFile("projetofinal/scores.dat");
			highScores = file.getHighestScores();
			highScores.add(score);
			Collections.sort(highScores);
			file.setHighestScores(highScores);
		} catch(IllegalArgumentException e) {
			e.printStackTrace();
			Messages.showFailure(null, "Failed to create file");
		} catch(IOException e) {
			e.printStackTrace();
			Messages.showFailure(null, "Fail to read file");
		}
	}

	/**
	 * Method responsible to set the strategy for accelerated buffer graphics.
	 *  
	 * @param strategy  The buffer for accelerated graphics
	 *
	 */
	public void setStrategy(BufferStrategy strategy) {
		this.strategy = strategy;
	}

	/**
	 * Method responsible to get the motherShipHints value
	 *  
	 * @return int
	 */
	public int getMotherShipHits() {
		return motherShipHits;
	}

	/**
	 * Method responsible to get the alienShipHints value
	 * 
	 * @return
	 */
	public int getAlienShipHits() {
		return alienShipHits;
	}

	/**
	 * Method responsible to set the numberOfAliens attribute value
	 * 
	 * @param numberOfAliens The value that will be set as numberOfAliens attribute
	 */
	public void setNumberOfAliens(int numberOfAliens) {
		this.numberOfAliens = numberOfAliens;
	}

	/**
	 * Method responsible to set the numberOfAliensShips attribute value
	 * 
	 * @param numberOfAlienShips The value that will be set as numberOfAliensShips attribute
	 */
	public void setNumberOfAlienShips(int numberOfAlienShips) {
		this.numberOfAlienShips = numberOfAlienShips;
	}

	/**
	 * Method responsible to set the scorePerLive attribute value
	 * 
	 * @param scorePerLive The value that will be set as scorePerLive attribute
	 */
	public void setScorePerLive(int scorePerLive) {
		this.scorePerLive = scorePerLive;
	}

	/**
	 * Method responsible to set the motherShipHints attribute value
	 * 
	 * @param motherShipHits The value that will be set as motherShipHints attribute
	 */
	public void setMotherShipHits(int motherShipHits) {
		this.motherShipHits = motherShipHits;
	}

	/**
	 * Method responsible to set alienShipHints attribute value
	 * 
	 * @param alienShipHits The value that will be set as alienShipHints attribute
	 */
	public void setAlienShipHits(int alienShipHits) {
		this.alienShipHits = alienShipHits;
	}

	/**
	 * A inner class to handle keyboard input events from the user
	 * on the game canvas. 
	 * 
	 * @author Felipe Knappe
	 */
	private class KeyInputHandler extends KeyAdapter {

		/**
		 * Notification that a key has been pressed. 
		 * 
		 * @param e The details of the key that was pressed 
		 */
		public void keyPressed(KeyEvent e) {

			/*
			 * Waiting for an "any key" typed then don't
			 * do anything with just a "press" 
			 */
			if (waitingForKeyPress) {
				return;
			}

			/*
			 * Events for player left move
			 */
			if (e.getKeyCode() == KeyEvent.VK_LEFT) {
				leftPressed = true;
			}
			/*
			 * Events for player right move
			 */
			if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
				rightPressed = true;
			}

			/*
			 * Events for player fire shoot
			 */
			if (e.getKeyCode() == KeyEvent.VK_SPACE) {
				firePressed = true;
			}

			/*
			 * Events for game pause
			 */
			if(e.getKeyCode() == KeyEvent.VK_P && !pausedGame) {
				pausedGame = true;
				notifyGamePaused();
			} else {
				pausedGame = false;
			}
		} 

		/**
		 * Notification that a key has been released.
		 *
		 * @param e The details of the key that was released
		 * 
		 */
		public void keyReleased(KeyEvent e) {
			/*
			 * Waiting for an "any key" typed then don't
			 * do anything with just a "released" 
			 */
			if (waitingForKeyPress) {
				return;
			}

			/*
			 * Events for player left move
			 */
			if (e.getKeyCode() == KeyEvent.VK_LEFT) {
				leftPressed = false;
			}

			/*
			 * Events for player right move
			 */
			if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
				rightPressed = false;
			}

			/*
			 * Eventos for player fire shoot
			 */
			if (e.getKeyCode() == KeyEvent.VK_SPACE) {
				firePressed = false;
			}
		}

		/**
		 * Notification that a key has been typed.
		 *
		 * @param e The details of the key that was typed. 
		 */
		public void keyTyped(KeyEvent e) {
			/*
			 *  Waiting for a "any key" type, then check
			 *  if received any events recently. Otherwise,
			 *  use pressCount flag to controls keyType() events
			 *  for player movements. 
			 */
			if (waitingForKeyPress) {
				if (pressCount == 1) {
					/*
					 *  Since a "any key" type event was received, 
					 *  "waitingForKeyPress" was set to false and
					 *  start the game
					 */
					waitingForKeyPress = false;
					gameStarted = true;
					pressCount = 0;
				} else {
					pressCount++;
				}
			}
		}
	}
}
